package org.sciom

import scala.collection.mutable.ArrayBuffer

abstract class Level(var message: ScIomMessage, var precedence: int)
case class AttachLevel(m: ScIomMessage, p: int) extends Level(m, p)
case class ArgLevel(m: ScIomMessage, p: int) extends Level(m, p)
case class NewLevel(m: ScIomMessage, p: int) extends Level(m, p)
case class UnusedLevel(m: ScIomMessage, p: int) extends Level(m, p)

object Levels {
  def createOperatorTable(state: ScIomState) : ScIomMap = {
    val ops = List(
	  ("@",   0),
	  ("@@",  0),
	  ("'",   0),
	  (".",   0),
	  ("?",   0),
	  ("(",   0),
	  (")",   0),

	  ("**",  1),
      
	  ("++",  2),
	  ("--",  2),

	  ("*",   3),
	  ("/",   3),
	  ("%",   3),
      
	  ("+",   4),
	  ("-",   4),

	  ("<<",  5),
	  (">>",  5),

	  (">",   6),
	  ("<",   6),
	  ("<=",  6),
	  (">=",  6),

	  ("==",  7),
	  ("!=",  7),

	  ("&",   8),

	  ("^",   9),

	  ("|",   10),

	  ("and", 11),
	  ("&&",  11),

	  ("or",  12),
	  ("||",  12),
      
	  ("..",  13),

	  ("+=",  14),
	  ("-=",  14),
	  ("*=",  14),
	  ("/=",  14),
	  ("%=",  14),
	  ("&=",  14),
	  ("^=",  14),
	  ("|=",  14),
	  ("<<=", 14),
	  (">>=", 14),
	  ("<-",  14),
	  ("<->", 14),
	  ("->",  14),

	  ("return", 15),

	  (",", 16)
    )

    val self = new ScIomMap(state)
    for( op <- ops )
      self.rawAtPut(state.symbolWithString(op._1), state.number(op._2))

    self
  }

  def createAssignOperatorTable(state: ScIomState) : ScIomMap = {
    val ops = List(
      (":=", "setSlot"),
      ("=", "updateSlot"),
      ("::=", "newSlot")
    )

    val self = new ScIomMap(state)
    for( op <- ops )
      self.rawAtPut(state.symbolWithString(op._1), state.symbolWithString(op._2))

    self
  }
}

class Levels(msg: ScIomMessage) {
  initBasedOn(msg) 

  def initBasedOn(msg: ScIomMessage) = {
    var state = msg.state
    var operatorTableSymbol = state.symbolWithString("OperatorTable")
    var opTable = msg.rawGetSlot(operatorTableSymbol)

    if(null == opTable) {
      opTable = state.core.rawGetSlot(operatorTableSymbol)

      if(null == opTable) {
        opTable = new ScIomObject(state)
        state.core.setSlotTo(operatorTableSymbol, opTable)
      }
    }

    var operatorsSymbol = state.symbolWithString("operators")
    var operators = opTable.rawGetSlot(operatorsSymbol)

    if(operators != null && operators.isInstanceOf[ScIomMap]) {
      operatorTable = operators.asInstanceOf[ScIomMap]
    } else {
      operatorTable = Levels.createOperatorTable(state)
      opTable.setSlotTo(operatorsSymbol, operatorTable)
    }

    var assignOperatorsSymbol = state.symbolWithString("assignOperators")
    var assignOperators = opTable.rawGetSlot(assignOperatorsSymbol)

    if(assignOperators != null && assignOperators.isInstanceOf[ScIomMap]) {
      assignOperatorTable = assignOperators.asInstanceOf[ScIomMap]
    } else {
      assignOperatorTable = Levels.createAssignOperatorTable(state)
      opTable.setSlotTo(assignOperatorsSymbol, assignOperatorTable)
    }
  }

  var operatorTable : ScIomMap = null
  var assignOperatorTable : ScIomMap = null

  var pool = new Array[Level](17)
  var currentLevel = 0
  var stack = new ArrayBuffer[Level]()

  reset()

  def reset() : Unit = {
    currentLevel = 1

    for( i <- 0 until pool.length ) 
      pool(i) = UnusedLevel(null, 0)
    
    pool(0) = NewLevel(null, 17)

    stack.clear

    stack += pool(0)
  }

  def nextMessage() = null // TODO: implement
  def attach(n: ScIomMessage, expressions: ArrayBuffer[ScIomMessage]) = null // TODO: implement

}
